/*******************************************************************************
 * Witz - Winboard Chess Engine                                                *
 * MOVEGEN.C                                                                   *
 *                                                                             *
 * Contains the move generator for the engine                                  *
 *                                                                             *
 * Please refer to the licence notice in MAIN.C                                *
 *                                                                             *
 * Copywright (C) 2006 Pradu Kannan                                            *
 * http://witz.sf.net/                                                         *
 ******************************************************************************/

#include "build.h"
#include "defs.h"
#include "protos.h"
#include "data.h"

//the move list for the main board
moveList mainMoveList;

/*generateLegalMoves()
 *generates all legal moves with move ordering
 *Note: this is much slower than regular generateMoves()
 *Note: do not use this in search as it is dead slow compared to
 *      the psuedo-legal move generator
 */
void generateLegalMoves(const board* pos, moveList* list)
{
	short i,j;
	TTprobe tt_probe;
	board tempboard;
	generateMoves(pos,list);
	for(i=list->moveCount-1;i>=0;i--)
	{
		tempboard=*pos;
		if(makemove(&tempboard,(list->moves[i]).m)) //Is illegalmove
		{
			for(j=i;j<list->moveCount;j++)
				list->moves[j]=list->moves[j+1];
			list->moveCount--;
		}
	}

	//find hashed move and put it in the front
	if(TT!=NULL && probeTT(pos,&tt_probe))
	{
		for(i=0;i<list->moveCount;i++)
			if(list->moves[i].m==tt_probe.m)
			{
				list->moves[i].score=INF;
				break;
			}
	}
	sort(list);
}

/*Pmove()
 *generates all possible pawn moves at a square for the current side
 *@param pos - the board position for which the pawn moves need to be generated for
 *@param index - where the pawn is at
 *
 *@returns a bitboard of all possible pawn moves
 */
U64 Pmove(const board* pos, const char index)
{
	U64 moves=Pmoves[pos->side][index]&~(pos->AllPieces);
	if(moves && (ROW(index)==1 || ROW(index)==6))
		moves|=Pmoves[pos->side][index+doubleMove2[pos->side]]&~(pos->AllPieces);
	moves|=Pcaps[pos->side][index]&pos->PiecesSide[pos->xside];
	return moves;
}

/*generateMoves()
 *generates all the moves with ordering for the current position
 *could be optimized, but too lazy to do it
 *DOES NOT SORT THE LIST
 *
 *@param pos - the board position
 *@param list - the move list
 */
void generateMoves(const board* pos, moveList* list)
{
	U64 pieceboard, moves;
	int from,to;
	move temp,temp2;
	list->moveCount=0;
	//Castling moves
	if(from=(pos->castling>>castlingPrivilageShift[pos->side])&0x3)
	{
		if(inCheck(*pos,pos->side))
			goto castlingdone;
		if(from&1 && !(castlingEmptySquares[pos->side][KINGSIDE] & (pos->AllPieces)))  //if Kingside available and pieces clear
		{
			moves=castlingPassover[pos->side][KINGSIDE];
			while(moves)
			{
				to=LastOne(moves);
				if(isAttacked(*pos,to,pos->side)) goto kdone;
				moves^=toBit[to];
			}
			list->moves[list->moveCount].m=castlingMoves[KINGSIDE];
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moves[list->moveCount].score+=CASTLING_BONUS;
			list->moveCount++;
		}
kdone:
		if(from&2 && !(castlingEmptySquares[pos->side][QUEENSIDE]&(pos->AllPieces)))  //if Queenside available and pieces clear
		{
			moves=castlingPassover[pos->side][QUEENSIDE];
			while(moves)
			{
				to=LastOne(moves);
				if(isAttacked(*pos,to,pos->side)) goto castlingdone;
				moves^=toBit[to];
			}
			list->moves[list->moveCount].m=castlingMoves[QUEENSIDE];
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moves[list->moveCount].score+=CASTLING_BONUS;
			list->moveCount++;
		}
	}
castlingdone:

	//EP moves
	if(pos->EP)
	{
		to=LastOne(pos->EP);
		pieceboard=Pcaps[pos->xside][to]&piecesSide(*pos,P);
		while(pieceboard)
		{
			from=LastOne(pieceboard);
			(list->moves[list->moveCount]).m=encodeTo(to)+encodePiece(P)+encodeCapture(P)+encodeEP+encodeFrom(from);
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moveCount++;
			pieceboard^=toBit[from];
		}
	}
	//Regular Moves
	pieceboard=piecesSide(*pos,P);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Pmove(pos,from);
		temp=encodeFrom(from)+encodePiece(P);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			if(ROW(to)==7 || ROW(to)==0) // if promotion generate the promotion pieces
			{
				temp2=list->moves[list->moveCount].m;
				list->moves[list->moveCount].m=temp2+encodePromotion(Q);
				list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
				list->moveCount++;
				list->moves[list->moveCount].m=temp2+encodePromotion(R);
				list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
				list->moveCount++;
				list->moves[list->moveCount].m=temp2+encodePromotion(B);
				list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
				list->moveCount++;
				list->moves[list->moveCount].m=temp2+encodePromotion(N);
			}
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,N);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Nmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(N);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,B);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Bmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(B);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,R);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Rmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(R);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,Q);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Qmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(Q);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	/*printf("MoveCount: %d\n",moveCount[depth]);
	for(to=0;to<moveCount[depth];to++)
		printf("%d)\t%s\t(%s)\n",to+1,fancyMoveToString(moveList[0][to]),moveToString(moveList[0][to]));
	printf("\n");*/
	pieceboard=piecesSide(*pos,K);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Kmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(K);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=scoreMove(pos,list->moves[list->moveCount].m);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
}

/*generateGoodCaptures()
 *generates good captures only and sorts it
 *
 *@param pos - the board position
 *@param list - the move list
 */
void generateGoodCaptures(const board* pos, moveList* list)
{
	U64 pieceboard, moves;
	int from,to;
	move temp,temp2;
	list->moveCount=0;

	//EP moves
	if(pos->EP)
	{
		to=LastOne(pos->EP);
		pieceboard=Pcaps[pos->xside][to]&piecesSide(*pos,P);
		while(pieceboard)
		{
			from=LastOne(pieceboard);
			list->moves[list->moveCount].m=encodeTo(to)+encodePiece(P)+encodeCapture(P)+encodeEP+encodeFrom(from);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			pieceboard^=toBit[from];
		}
	}

	//Pawn Promotion
	pieceboard=piecesSide(*pos,P)&rank7[pos->side];
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Pmove(pos,from);
		temp=encodeFrom(from)+encodePiece(P);
		while(moves)
		{
			to=LastOne(moves);
			temp2=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].m=temp2+encodePromotion(Q);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			list->moves[list->moveCount].m=temp2+encodePromotion(R);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			list->moves[list->moveCount].m=temp2+encodePromotion(B);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			list->moves[list->moveCount].m=temp2+encodePromotion(N);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}

	//Regular Moves

	//Pawns
	if(pos->xside) //WHITE to play
	{
		pieceboard=piecesWHITE(*pos,P)&~rank7[WHITE];

		//A1H8
		moves=((pieceboard&0x7F7F7F7F7F7F7F7FULL)<<9)&pos->PiecesSide[BLACK];
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=encodePiece(P)+encodeFrom(to-9)+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
		
		//H1A8
		moves=((pieceboard&0xFEFEFEFEFEFEFEFEULL)<<7)&pos->PiecesSide[BLACK];
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=encodePiece(P)+encodeFrom(to-7)+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
	}
	else //BLACK to play
	{
		pieceboard=piecesBLACK(*pos,P)&~rank7[BLACK];

		//A1H8
		moves=((pieceboard&0xFEFEFEFEFEFEFEFEULL)>>9)&pos->PiecesSide[WHITE];
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=encodePiece(P)+encodeFrom(to+9)+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}

		//H1A8
		moves=((pieceboard&0x7F7F7F7F7F7F7F7FULL)>>7)&pos->PiecesSide[WHITE];
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=encodePiece(P)+encodeFrom(to+7)+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
	}

	pieceboard=piecesSide(*pos,N);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Nmoves(*pos,from)&pos->PiecesSide[pos->xside];
		temp=encodeFrom(from)+encodePiece(N);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,B);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Bmoves(*pos,from)&pos->PiecesSide[pos->xside];
		temp=encodeFrom(from)+encodePiece(B);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,R);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Rmoves(*pos,from)&pos->PiecesSide[pos->xside];
		temp=encodeFrom(from)+encodePiece(R);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,Q);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Qmoves(*pos,from)&pos->PiecesSide[pos->xside];
		temp=encodeFrom(from)+encodePiece(Q);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,K);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Kmoves(*pos,from)&pos->PiecesSide[pos->xside];
		temp=encodeFrom(from)+encodePiece(K);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moves[list->moveCount].score=SEE(pos,list->moves[list->moveCount].m);
			if(list->moves[list->moveCount].score>=0)
				list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	sort(list);
}

/*generatePerftMoves()
 *generates all the moves without ordering for the current position
 *could be optimized, but too lazy to do it
 */
void generatePerftMoves(const board* pos, moveList* list)
{
	U64 pieceboard, moves;
	int from,to;
	move temp,temp2;
	list->moveCount=0;
	//Castling moves
	if(from=(pos->castling>>castlingPrivilageShift[pos->side])&0x3)
	{
		if(inCheck(*pos,pos->side))
			goto castlingdone;
		if(from&1 && !(castlingEmptySquares[pos->side][KINGSIDE] & (pos->AllPieces)))  //if Kingside available and pieces clear
		{
			moves=castlingPassover[pos->side][KINGSIDE];
			while(moves)
			{
				to=LastOne(moves);
				if(isAttacked(*pos,to,pos->side)) goto kdone;
				moves^=toBit[to];
			}
			(list->moves[list->moveCount]).m=castlingMoves[KINGSIDE];
			list->moveCount++;
		}
kdone:
		if(from&2 && !(castlingEmptySquares[pos->side][QUEENSIDE]&(pos->AllPieces)))  //if Queenside available and pieces clear
		{
			moves=castlingPassover[pos->side][QUEENSIDE];
			while(moves)
			{
				to=LastOne(moves);
				if(isAttacked(*pos,to,pos->side)) goto castlingdone;
				moves^=toBit[to];
			}
			(list->moves[list->moveCount]).m=castlingMoves[QUEENSIDE];
			list->moveCount++;
		}
	}
castlingdone:
	//EP moves
	if(pos->EP)
	{
		to=LastOne(pos->EP);
		pieceboard=Pcaps[pos->xside][to]&piecesSide(*pos,P);
		while(pieceboard)
		{
			from=LastOne(pieceboard);
			(list->moves[list->moveCount]).m=encodeTo(to)+encodePiece(P)+encodeCapture(P)+encodeEP+encodeFrom(from);
			list->moveCount++;
			pieceboard^=toBit[from];
		}
	}

	//Regular Moves
	pieceboard=piecesSide(*pos,P);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Pmove(pos,from);
		temp=encodeFrom(from)+encodePiece(P);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			if(ROW(to)==7 || ROW(to)==0) // if promotion generate the promotion pieces
			{
				temp2=list->moves[list->moveCount].m;
				list->moves[list->moveCount].m=temp2+encodePromotion(Q);
				list->moveCount++;
				list->moves[list->moveCount].m=temp2+encodePromotion(R);
				list->moveCount++;
				list->moves[list->moveCount].m=temp2+encodePromotion(B);
				list->moveCount++;
				list->moves[list->moveCount].m=temp2+encodePromotion(N);
			}
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,N);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Nmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(N);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,B);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Bmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(B);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,R);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Rmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(R);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,Q);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Qmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(Q);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
	pieceboard=piecesSide(*pos,K);
	while(pieceboard)
	{
		from=LastOne(pieceboard);
		moves=Kmoves(*pos,from)&~(pos->PiecesSide[pos->side]);
		temp=encodeFrom(from)+encodePiece(K);
		while(moves)
		{
			to=LastOne(moves);
			list->moves[list->moveCount].m=temp+encodeTo(to)+encodeCapture(pos->PieceTypes[to]);
			list->moveCount++;
			moves^=toBit[to];
		}
		pieceboard^=toBit[from];
	}
}
